package com.intretech.tj.tcp;

import com.intretech.tj.global.exception.IPErrorException;
import com.intretech.tj.global.parent.VerticleParent;
import com.intretech.tj.tcp.trigger.LinkTrigger;
import com.intretech.tj.tcp.trigger.MessageTrigger;
import io.smallrye.mutiny.Uni;
import io.vertx.core.net.NetServerOptions;
import io.vertx.core.buffer.Buffer;
import io.vertx.mutiny.core.net.NetSocket;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import lombok.extern.slf4j.Slf4j;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import static com.intretech.tj.global.tools.HexTool.hexStrToByteArray;
import static com.intretech.tj.tcp.OutLog.*;

@Slf4j
@ApplicationScoped
public class TCPVerticle extends VerticleParent {
    /**
     * 日志输出
     */
    private final OutLog outLog;

    /**
     * 配置文件
     */
    private final TCPConfig config;

    /**
     * 连接触发器
     */
    private final LinkTrigger likeTrigger;

    /**
     * 消息触发器
     */
    private final MessageTrigger messageTrigger;

    /**
     * 连接映射<br/>
     * key:连接设备ip<br/>
     * value:eventBus address
     */
    private final Map<String, String> socketMap = new ConcurrentHashMap<>();

    @Inject
    public TCPVerticle(OutLog outLog, TCPConfig config, LinkTrigger likeTrigger, MessageTrigger messageTrigger) {
        this.outLog = outLog;
        this.config = config;
        this.likeTrigger = likeTrigger;
        this.messageTrigger = messageTrigger;
    }

    @Override
    public Uni<Void> asyncStart() {
        log.info(outLog.getMessage(ServerStart));
        return vertx.createNetServer(new NetServerOptions().setRegisterWriteHandler(true))
                .connectHandler(this::connectHandler)
                .listen(config.port())
                .replaceWithVoid();
    }

    /**
     * 连接处理器
     *
     * @param socket 套接字
     */
    private void connectHandler(NetSocket socket) {
        String ip = socket.remoteAddress().toString();
        socketMap.put(ip, socket.writeHandlerID());
        log.info(outLog.getMessage(Connect, ip));

        // 连接触发器
        likeTrigger.trigger(ip, socket);

        socket.handler(buffer -> messageHandler(ip, buffer.toString(), socket));
        socket.closeHandler(() -> closeHandler(socket));
    }

    /**
     * 消息处理器
     *
     * @param ip      设备ip
     * @param message 消息
     * @param socket  套接字
     */
    private void messageHandler(String ip, String message, NetSocket socket) {
        if (message.equalsIgnoreCase("exit"))
            socket.endAndForget();
        else
            // 消息触发器
            messageTrigger.trigger(ip, message);
    }

    /**
     * 断开处理器
     *
     * @param socket 套接字
     */
    private void closeHandler(NetSocket socket) {
        String ip = socket.remoteAddress().toString();
        socketMap.remove(ip);
        log.info(outLog.getMessage(Close, ip));
    }

    /**
     * 发送消息
     *
     * @param address 设备ip与端口
     * @param message 消息内容
     * @throws IPErrorException ip没有找到时抛出
     */
    public void send(String address, String message) throws IPErrorException {
        if (socketMap.containsKey(address))
            vertx.eventBus().send(socketMap.get(address), Buffer.buffer(hexStrToByteArray(message)));
        else {
            log.warn(outLog.getMessage(NotFoundIP, address));
            throw new IPErrorException(IPError);
        }
    }
}
